#include "ParserDefines.hpp"
#include "InputStream.hpp"
#include "lexer.hpp"
#include "Exception.hpp"
#include "Options.hpp"
#include "CommandLineErrorException.hpp"
#include "CompileException.hpp"
#include "ScriptException.hpp"
#include "RunTimeException.hpp"

enum ExitCode
{
	NoError = 0,           ///< Script parsing and executed completed normally
	CommandLineError = 1,  ///< Compiler was invoked with invalid command line
	CompilationError = 2,  ///< Error occurred during script parsing
	ExecutionError   = 3,  ///< Error occurred during script execution
	InternalError    = 4,  ///< Error occurred inside compiler's internals
	UnknownError     = 5,  ///< Something else went wrong
};

class ostream_ptr
{
public:
	ostream_ptr(String fileName) : ptr_(), owns_(false)
	{
		if(fileName.empty())
			ptr_= &std::cout;
		else
		{
			owns_ = true;
			ptr_ = new std::fstream(fileName, std::ios_base::out);
		}
	}

	~ostream_ptr()
	{
		if(owns_)
		{
			delete ptr_;
		}
	}
	
	std::ostream * get() const { return ptr_; }
private:
	std::ostream * ptr_;
	bool owns_;
};

int showHelp();
int showVersion();

int main(int argc, char * argv[])
{
	try
	{
		setlocale(LC_ALL, "");

		Options options(argc, argv);

		if(options.showVersion()) return showVersion();
		if(options.showHelp()) return showHelp();
		
		ptr<InputStream> istream;
		if(options.inputFile().empty())
			istream = new InputStream(&std::cin, "stdin");
		else
			istream = new InputStream(options.inputFile());

		ostream_ptr ostream(options.outputFile());
		
		Lexer lexer(istream.get());
		yy::parser parser(&lexer, ostream.get());
#if YYDEBUG
		parser.set_debug_level(1);
#endif
		int retVal = parser.parse();
		return retVal ? retVal : NoError;
	}
	catch(CommandLineErrorException const & ex)
	{
		std::cerr << ex << std::endl;
		return CommandLineError;
	}
	catch(CompileException const & ex)
	{
		std::cerr << ex << std::endl;
		return CompilationError;
	}
	catch(ScriptExceptionException const & ex)
	{
		std::cerr << ex << std::endl;
		return ExecutionError;
	}
	catch(RunTimeException const & ex)
	{
		std::cerr << ex << std::endl;
		return InternalError;
	}
	catch(Exception const & ex)
	{
		std::cerr << ex << std::endl;
		return UnknownError;
	}
	catch(std::exception const & ex)
	{
		std::cerr << ex.what() << std::endl;
		return UnknownError;
	}
}

int showVersion()
{
	std::cout << "Monkey Coding Script Engine 0.1a" << std::endl;
	std::cout << "by Nickolas V. Pohilets" << std::endl;
	std::cout << "(c) 2009" << std::endl;
	return 0;
}

int showHelp()
{
	std::cout << "Usage: mcs.exe [-o file] [file.mcs]" << std::endl;
	std::cout << "Executes script from <file.mcs> and writes produced output to <file>." << std::endl;
	std::cout << "If input and/or output files are not specified I/O defaults to stdin/stdout." << std::endl;
	return 0;
}